<?php


namespace App\Utils;


use App\Constants\ErrorCode;
use App\Exception\BusinessException;
use Casbin\Enforcer;
use CasbinAdapter\Database\Adapter;

class Casbin
{

    public $enforerInstance;

    protected $adapterInstance;

    public $rolePrefix;

    public $userPrefix;

    public $permissionDelimiter = '->';

    public function __construct(array $config)
    {
        $adapter = new Adapter(
            [
                'type' => env('DB_DRIVER'), // mysql,pgsql,sqlite,sqlsrv
                'hostname' => env('DB_HOST'),
                'database' => env('DB_DATABASE'),
                'username' => env('DB_USERNAME'),
                'password' => env('DB_PASSWORD'),
                'hostport' => env('DB_PORT'),
            ]
        );
        $this->adapterInstance = $adapter;
        $this->enforerInstance = new Enforcer($config['model_path'], $adapter);

        $this->userPrefix = $config['user_prefix'];
        $this->rolePrefix = $config['role_prefix'];
        $this->permissionDelimiter = $config['permission_delimiter'];
    }

    /**
     * 设置用户权限
     *
     * @param mixed $userId
     * @param array $roles
     * @return void
     */
    public function setRolesForUser($userId, array $roles)
    {
        $this->enforerInstance->deleteRolesForUser($this->formatUserId($userId));
        foreach ($roles as $roleId) {
            $status = $this->enforerInstance->addRoleForUser($this->formatUserId($userId), $this->formatRoleId($roleId));
            if(!$status){
                throw new BusinessException(ErrorCode::SET_ROLE_FOR_USER_ERROR);
            }
        }
    }

    /**
     * 获取用户权限
     *
     * @param mixed $userId
     * @return array
     */
    public function getRolesByUser($userId): array
    {
        $roles = $this->enforerInstance->getRolesForUser($this->formatUserId($userId));
        return $this->decryptArray($roles, $this->rolePrefix);
    }

    /**
     * 设置角色权限
     *
     * @param mixed $roleId
     * @param array $resources
     * @return void
     */
    public function setPermissionsForRole($roleId, array $resources)
    {
        $this->enforerInstance->deletePermissionsForUser($this->formatRoleId($roleId));
        foreach ($resources as $resource) {
            [$path, $action] = explode($this->permissionDelimiter, $resource);
            $status = $this->enforerInstance->addPermissionForUser($this->formatRoleId($roleId), $path, strtolower($action));
            if(!$status){
                throw new BusinessException(ErrorCode::SET_ROLE_PERMISSION_ERROR);
            }
        }
    }

    /**
     * 获取角色权限
     *
     * @param mixed $roleId
     * @return void
     */
    public function getPermissionsByRole($roleId): array
    {
        $permissions = $this->enforerInstance->getPermissionsForUser($this->formatRoleId($roleId));

        $result = [];
        foreach ($permissions as $p) {
            $result[] = $p[1] . $this->permissionDelimiter . strtolower($p[2]);
        }
        return $result;
    }

    /**
     * 根据角色获取用户
     *
     * @param mixed $roleId
     * @return void
     */
    public function getUsersByRole($roleId): array
    {
        $users = $this->enforerInstance->getUsersForRole($this->formatRoleId($roleId));
        return $this->decryptArray($users, $this->userPrefix);
    }

    /**
     * 删除用户
     * @param mixed $userId
     * @return void
     */
    public function deleteUser($userId): void
    {
        // $this->enforerInstance->deletePermissionsForUser('role' . $userId);
        $this->enforerInstance->deleteUser($this->formatUserId($userId));
    }

    /**
     * 删除角色
     *
     * @param mixed $roleId
     * @return void
     */
    public function deleteRole($roleId): void
    {
        $this->enforerInstance->deletePermissionsForUser($this->formatRoleId($roleId));
        $this->enforerInstance->deleteRole($this->formatRoleId($roleId));
    }

    /**
     * 删除权限
     *
     * @param string $resource
     * @param string $action
     * @return void
     */
    public function deletePermission(string $resource, string $action): bool
    {
        return $this->enforerInstance->deletePermission($resource, $action);
    }

    /**
     * @param $sub 'user_id'
     * @param $obj 'uri base path'
     * @param $act 'method'
     * @return bool
     * @throws \Casbin\Exceptions\CasbinException
     */
    public function enforce($sub, $obj, $act): bool
    {
        return $this->enforerInstance->enforce($sub, $obj, strtolower($act));
    }

    public function can(int $userId,string $basePath,string $method):bool
    {
        return $this->enforce($this->formatUserId($userId),$basePath,$method);
    }


    private function decryptArray(array $array, string $padChar): array
    {
        foreach ($array as &$item) {
            $item = intval(str_replace($padChar, '', $item));
        }
        unset($item);
        return $array;
    }

    private function formatUserId(int $userId): string
    {
        return $this->userPrefix . $userId;
    }

    private function formatRoleId(int $roleId): string
    {
        return $this->rolePrefix . $roleId;
    }


}